/**
 * @file      userid_manager.c
 * @brief
 * @version   1.0
 * @date      2023.12.14
 *
 * @copyright Copyright (c) 2018 UCchip Technology Co.,Ltd. All rights reserved.
 *
 * Change Logs:
 * Date          Author       Notes
 * 2023.12.14    ucchip       the first version
 *
 */

#include "userid_manager.h"
#include "manager_list.h"
#include "time_slot_manager.h"
#include "uc_wiota_api.h"
#include <rtthread.h>
#define DBG_ENABLE
#define DBG_SECTION_NAME "id_mn"
#include "uc_log.h"

// WIoTa最大的slot数为8
#define SLOT_NUM (8)

// user id管理哈希值
#define HASH_LIST_LEN 8
#define HASH_LIST_KEY(addr) (addr & (HASH_LIST_LEN - 1))

/**
 * @brief user id管理资源结构体
 *
 */
typedef struct
{
    manager_list_t mng_userid_list[SLOT_NUM][HASH_LIST_LEN]; /**< id管理哈希链表，每个slot上都有一条链 */
    unsigned int count;                                      /**< 单条链表节点计数 */
} mng_userid_resource_t;

// user id管理资源
static mng_userid_resource_t *g_mng_userid_resource = RT_NULL;
// 当前已分配的id的burst idx
static unsigned char g_reallocated_burst_idx = 0;
// 当前已分配的id的slot idx
static unsigned char g_reallocated_slot_idx = 0;
// 当前配置下WIoTa的帧长
static unsigned int g_frame_len = 0;
// 当前配置下WIoTa的总的上行子帧个数
static unsigned short g_subframe_num = 0;
// 当前分配的id个数
static unsigned int g_reallocated_num = 0;
static unsigned int g_actually_num = 0;

/**
 * @brief 获取已分配的id个数
 *
 * @return unsigned int
 */
unsigned int manager_get_reallocated_num(void)
{
    return g_reallocated_num;
}

/**
 * @brief 获取实际分配的id总个数
 *
 * @return unsigned int
 */
unsigned int manager_get_actually_num(void)
{
    return g_actually_num;
}

/**
 * @brief 根据当前配置计算总的上行子帧个数，id管理初始化使用
 *
 */
static void manager_ul_subframe_num_init(void)
{
    sub_system_config_t config;

    uc_wiota_get_system_config(&config);

    g_subframe_num = (1 << config.group_number) * 8;
    TRACE_I("subframe_num %d", g_subframe_num);
}

/**
 * @brief 获取WIoTa的帧长，分时发送，分配时隙时使用
 *
 */
static void manager_frame_len_init(void)
{
    g_frame_len = uc_wiota_get_frame_len();
    TRACE_I("frame_len %d", g_frame_len);
}

/**
 * @brief 获取上行子帧个数
 *
 * @return unsigned int 上行子帧个数
 */
unsigned int manager_get_ul_subframe_num(void)
{
    return g_subframe_num;
}

/**
 * @brief 获取帧长
 *
 * @return unsigned int 帧长
 */
unsigned int manager_get_frame_len(void)
{
    return g_frame_len;
}

/**
 * @brief 子帧idx管理和分时时隙分配
 *
 */
static void manager_subframe_idx_inc(void)
{
    g_reallocated_burst_idx++;
    if (g_reallocated_burst_idx > g_subframe_num - 1)
    {
        g_reallocated_burst_idx = 0;
        g_reallocated_slot_idx++;
        if (g_reallocated_slot_idx > SLOT_NUM - 1)
        {
            g_reallocated_slot_idx = 0;
        }
        manager_update_ul_time_slot_fn();
    }
}

/**
 * @brief 链表查询回调，用于判断id是否已经分配
 *
 */
static int manager_query_element_dev_addr(manager_list_t *node, void *target)
{
    unsigned int *wiota_addr = target;
    manager_address_t *data = node->data;

    if (data->allocate_flag == ADDRESS_PREALLOCATED &&
        (*wiota_addr & 0xFFFF) == data->wiota_id)
    {
        return 0;
    }
    return 1;
}

/**
 * @brief 链表查询回调，用于判断id是否已经分配
 *
 */
static int manager_query_element_allocate(manager_list_t *node, void *target)
{
    unsigned int *mac = target;
    manager_address_t *data = node->data;

    if (data->allocate_flag == ADDRESS_PREALLOCATED &&
        (*mac == data->dev_id || (data->wiota_id | (1 << 31)) == *mac))
    {
        return 0;
    }
    return 1;
}

/**
 * @brief 链表查询回调，用于判断id是否未分配
 *
 */
static int manager_counter_element_reserved(manager_list_t *node, void *target)
{
    manager_address_t *data = node->data;
    if (data->allocate_flag == ADDRESS_RESERVED)
    {
        return 0;
    }
    return 1;
}

/**
 * @brief id管理地址初始化
 *
 */
static void manager_address_init(void)
{
    // 计算上行子帧个数和获取帧长
    manager_ul_subframe_num_init();
    manager_frame_len_init();

    RT_ASSERT(RT_NULL == g_mng_userid_resource);

    // 根据子帧个数，分配id管理资源
    g_mng_userid_resource = rt_malloc(sizeof(mng_userid_resource_t) * g_subframe_num);
    RT_ASSERT(g_mng_userid_resource);

    // 初始化id管理链表
    for (int subf_idx = 0; subf_idx < g_subframe_num; subf_idx++)
    {
        for (int slot_idx = 0; slot_idx < SLOT_NUM; slot_idx++)
        {
            for (int m = 0; m < HASH_LIST_LEN; m++)
            {
                init_manager_list(&g_mng_userid_resource[subf_idx].mng_userid_list[slot_idx][m]);
                g_mng_userid_resource[subf_idx].count = 0;
            }
        }
    }
}

/**
 * @brief 计算start_addree~end_addree的地址段的所有id的位置，并将其中完全平铺的addr_num个节点添加到id管理资源中，
 *        用于IoTE的鉴权后根据IoTE的设备id分配WIoTa id，理论上地址段的id个数应该远远大于addr_num的个数才能保证完
 *        全平铺
 *
 * @return int 0表示成功，非0表示失败
 */
static int manager_calculate_userid_pos(id_manager_cfg_t *id_mng_cfg)
{
    RT_ASSERT(g_mng_userid_resource);

    uc_dev_pos_t *dev_pos_array;
    int all_count = 0;
    int insert_num = 0;
    unsigned int start_addree = 0;
    unsigned int end_addree = 0;
    unsigned int addr_num = 0;
    unsigned short subframe_num = 0;
    unsigned int min_addr_num = 0;
    unsigned int actual_addr_num = 0;
    unsigned int same_subframe_num = 0;
    unsigned int same_slot_num = 0;
    unsigned int *id_array = RT_NULL;
    unsigned char(*pos)[SLOT_NUM] = RT_NULL;
    unsigned char group_idx = 0;
    unsigned char burst_idx = 0;
    unsigned char slot_idx = 0;

    // 根据静态数据拿到一个id地址段
    start_addree = id_mng_cfg->assign_start_addr;
    end_addree = id_mng_cfg->assign_end_addr;
    addr_num = id_mng_cfg->addr_num;
    // 规定WIoTa id最高bit位为1
    start_addree |= (1 << 31);
    end_addree |= (1 << 31);
    // 地址段计数
    all_count = (end_addree - start_addree + 1);

    subframe_num = g_subframe_num;
    min_addr_num = subframe_num * SLOT_NUM;
    actual_addr_num = (addr_num / min_addr_num + (addr_num % min_addr_num == 0 ? 0 : 1)) * min_addr_num;
    same_subframe_num = actual_addr_num / subframe_num;
    same_slot_num = same_subframe_num / SLOT_NUM;

    TRACE_I("reserved_addr 0x%x~0x%x, all_count %d, addr_num %d, subframe_num %d",
            start_addree, end_addree, all_count, addr_num, subframe_num);
    TRACE_I("min_addr_num %d, actual_addr_num %d, same_subframe_num %d, same_slot_num %d",
            min_addr_num, actual_addr_num, same_subframe_num, same_slot_num);

    RT_ASSERT(all_count >= actual_addr_num);
    RT_ASSERT(same_subframe_num % 8 == 0);

    // 生成all_count个最高bit位为1的id
    id_array = rt_malloc(all_count * 4);
    RT_ASSERT(id_array);
    for (int i = 0; i < all_count; i++)
    {
        id_array[i] = start_addree + i;
    }

    // 记录位置分配个数的数组指针
    pos = (unsigned char(*)[SLOT_NUM])rt_malloc(subframe_num * SLOT_NUM);
    RT_ASSERT(pos);
    rt_memset(pos, 0, subframe_num * SLOT_NUM);

    // 查询一组id在帧结构的位置
    dev_pos_array = uc_wiota_query_dev_pos_by_userid(id_array, all_count);
    if (dev_pos_array == RT_NULL)
    {
        TRACE_E("uc_wiota_query_dev_pos_by_userid fail");
        rt_free(pos);
        rt_free(id_array);
        return 1;
    }

    // 节点插入规则，保证每个子帧位置id个数相同，保证每个slot上都有终端且数量相同
    for (int i = 0; i < all_count; i++)
    {
        group_idx = dev_pos_array[i].group_idx;
        burst_idx = dev_pos_array[i].burst_idx + group_idx * 8;
        slot_idx = dev_pos_array[i].slot_idx;

        if (pos[burst_idx][slot_idx] < same_slot_num)
        {
            // 每当有一个平铺位置，开辟一个节点保存并存入id管理资源中
            manager_address_t *manager_address_node = rt_malloc(sizeof(manager_address_t));
            RT_ASSERT(manager_address_node);
            rt_memset(manager_address_node, 0, sizeof(manager_address_t));

            manager_address_node->wiota_id = (id_array[i] & 0xFFFF);
            manager_address_node->allocate_flag = ADDRESS_RESERVED;

            insert_head_manager_list(&g_mng_userid_resource[burst_idx].mng_userid_list[slot_idx][HASH_LIST_KEY(manager_address_node->wiota_id)],
                                     manager_address_node);
            pos[burst_idx][slot_idx]++;
            g_mng_userid_resource[burst_idx].count++;
            insert_num++;
        }
        // 当插入节点的数量已经满足用户设定的需求时，退出循环，不在添加
        if (insert_num == actual_addr_num)
        {
            break;
        }
    }

    // 如果插入节点的数量不满足用户设定的需求，提示让用户增大地址段结束地址重新初始化
    if (insert_num < addr_num)
    {
        TRACE_W("insert_num < actual_addr_num, please inc reserved_end_addr");
        // rt_thread_mdelay(1000);
        // RT_ASSERT(0); // TODO:
    }
    g_actually_num = insert_num;
    TRACE_I("insert wiota_id num %d", insert_num);
    rt_free(id_array);
    rt_free(pos);
    rt_free(dev_pos_array);

    return 0;
}

/**
 * @brief 根据IoTE的设备id重新分配一个用于鉴权后通讯的WIoTa id
 *
 * @param dev_address          终端的dev id
 * @return manager_address_t*  地址管理结构体指针
 */
manager_address_t *manager_reallocated_wiota_id(unsigned int dev_address)
{
    int subf_idx;
    manager_address_t *reserve_addr = RT_NULL;
    manager_list_t *op_node = RT_NULL;

    if (g_mng_userid_resource == RT_NULL)
    {
        TRACE_E("g_mng_userid_resource is null");
        return RT_NULL;
    }

    // 遍历查找，该dev_address是否已经分配了WIoTa id，如果已经分配，返回该管理地址结构体
    for (subf_idx = 0; subf_idx < g_subframe_num; subf_idx++)
    {
        for (int m = HASH_LIST_KEY(dev_address); m < HASH_LIST_KEY(dev_address) + HASH_LIST_LEN; m++)
        {
            for (int slot_idx = 0; slot_idx < SLOT_NUM; slot_idx++)
            {
                op_node = query_head_list(&g_mng_userid_resource[subf_idx].mng_userid_list[slot_idx][m % HASH_LIST_LEN],
                                          &dev_address,
                                          manager_query_element_allocate);
                if (RT_NULL != op_node)
                {
                    reserve_addr = op_node->data;
                    return reserve_addr;
                }
            }
        }
    }

    // 未分配，则根据规则分配一个，分配规则为slot到7后burst加1，burst到最大（g_subframe_num）后，group加1，group到最大后重复上诉步骤
    for (subf_idx = 0; subf_idx < g_subframe_num; subf_idx++)
    {
        int the_count = g_mng_userid_resource[g_reallocated_burst_idx].count;

        if (the_count)
        {
            for (int m = HASH_LIST_KEY(dev_address); m < HASH_LIST_KEY(dev_address) + HASH_LIST_LEN; m++)
            {
                op_node = query_head_list(&g_mng_userid_resource[g_reallocated_burst_idx].mng_userid_list[g_reallocated_slot_idx][m % HASH_LIST_LEN],
                                          RT_NULL,
                                          manager_counter_element_reserved);
                if (RT_NULL == op_node)
                {
                    continue;
                }
                else
                {
                    break;
                }
            }

            if (RT_NULL == op_node)
            {
                manager_subframe_idx_inc();
                continue;
            }

            reserve_addr = op_node->data;

            if (0 != manager_add_recv_send_by_fn_info(reserve_addr->wiota_id | (1 << 31), manager_get_ul_time_slot_fn()))
            {
                return NULL;
            }

            reserve_addr->allocate_flag = ADDRESS_PREALLOCATED;
            reserve_addr->burst_idx = g_reallocated_burst_idx;
            reserve_addr->slot_idx = g_reallocated_slot_idx;
            reserve_addr->time_slot_fn = manager_get_ul_time_slot_fn();
            reserve_addr->dev_id = dev_address;

            if (g_mng_userid_resource[g_reallocated_burst_idx].count > 0)
                g_mng_userid_resource[g_reallocated_burst_idx].count--;

            manager_subframe_idx_inc();

            g_reallocated_num++;

            return reserve_addr;
        }
        else
        {
            manager_subframe_idx_inc();
        }
    }

    return RT_NULL;
}

/**
 * @brief 通过WIoTa id查询该id是否存在于管理链表中
 *
 * @param wiota_id            终端的WIoTa id
 * @return manager_address_t* 存在返回节点地址，不存在返回RT_NULL
 */
manager_address_t *manager_query_reserve_addr_by_wiota_id(unsigned int wiota_id)
{
    manager_address_t *reserve_data = RT_NULL;
    manager_list_t *node = RT_NULL;

    for (int subf_idx = 0; subf_idx < g_subframe_num; subf_idx++)
    {
        for (int m = 0; m < HASH_LIST_LEN; m++)
        {
            for (int slot_idx = 0; slot_idx < SLOT_NUM; slot_idx++)
            {
                node = query_head_list(&g_mng_userid_resource[subf_idx].mng_userid_list[slot_idx][m],
                                       &wiota_id,
                                       manager_query_element_dev_addr);
                if (RT_NULL != node)
                {
                    reserve_data = node->data;
                    return reserve_data;
                }
            }
        }
    }

    return RT_NULL;
}

/**
 * @brief 通过dev id查询该id是否存在于管理链表中
 *
 * @param dev_id              终端的dev id
 * @return manager_address_t* 存在返回节点地址，不存在返回RT_NULL
 */
manager_address_t *manager_query_reserve_addr_by_dev_id(unsigned int dev_id)
{
    manager_address_t *reserve_data = RT_NULL;
    manager_list_t *node = RT_NULL;

    for (int subf_idx = 0; subf_idx < g_subframe_num; subf_idx++)
    {
        for (int m = HASH_LIST_KEY(dev_id); m < HASH_LIST_KEY(dev_id) + HASH_LIST_LEN; m++)
        {
            for (int slot_idx = 0; slot_idx < SLOT_NUM; slot_idx++)
            {
                node = query_head_list(&g_mng_userid_resource[subf_idx].mng_userid_list[slot_idx][m % HASH_LIST_LEN],
                                       &dev_id,
                                       manager_query_element_allocate);
                if (RT_NULL != node)
                {
                    reserve_data = node->data;
                    return reserve_data;
                }
            }
        }
    }

    return RT_NULL;
}

/**
 * @brief 通过一个dev id查询对应的WIoTa id
 *
 * @param dev_id        终端的dev id
 * @return unsigned int 存在返回该WIoTa id，不存在返回0
 */
unsigned int manager_query_wiota_id_by_dev_id(unsigned int dev_id)
{
    RT_ASSERT(g_mng_userid_resource);

    if (0 == dev_id)
    {
        TRACE_E("invalid dev_id 0x%x", dev_id);
        return 0;
    }

    manager_list_t *node = RT_NULL;

    // 遍历查找，找到后返回地址，未找到返回0
    for (int subf_idx = 0; subf_idx < g_subframe_num; subf_idx++)
    {
        for (int m = HASH_LIST_KEY(dev_id); m < HASH_LIST_KEY(dev_id) + HASH_LIST_LEN; m++)
        {
            for (int slot_idx = 0; slot_idx < SLOT_NUM; slot_idx++)
            {
                node = query_head_list(&g_mng_userid_resource[subf_idx].mng_userid_list[slot_idx][m % HASH_LIST_LEN],
                                       &dev_id,
                                       manager_query_element_allocate);
                if (RT_NULL != node)
                {
                    manager_address_t *reserve_data = node->data;
                    return (reserve_data->wiota_id | (1 << 31));
                }
            }
        }
    }
    TRACE_E("query_wiota_id_by_dev_id is error, dev_id 0x%x(%d) ", dev_id, dev_id);
    return 0;
}

/**
 * @brief 通过一个WIoTa id查询对应的dev id
 *
 * @param wiota_id      终端的WIoTa id
 * @return unsigned int 存在返回dev id，不存在返回0
 */
unsigned int manager_query_dev_id_by_wiota_id(unsigned int wiota_id)
{
    RT_ASSERT(g_mng_userid_resource);
    RT_ASSERT(0 != wiota_id);

    manager_list_t *node = RT_NULL;

    // 遍历查找，找到后返回地址，未找到返回0
    for (int subf_idx = 0; subf_idx < g_subframe_num; subf_idx++)
    {
        for (int m = 0; m < HASH_LIST_LEN; m++)
        {
            for (int slot_idx = 0; slot_idx < SLOT_NUM; slot_idx++)
            {
                node = query_head_list(&g_mng_userid_resource[subf_idx].mng_userid_list[slot_idx][m],
                                       &wiota_id,
                                       manager_query_element_dev_addr);
                if (RT_NULL != node)
                {
                    manager_address_t *reserve_data = node->data;
                    return reserve_data->dev_id;
                }
            }
        }
    }
    TRACE_E("query_dev_id_by_wiota_id error, wiota_id 0x%x(%d) ", wiota_id, wiota_id);
    return 0;
}

/**
 * @brief id管理反初始化
 *
 */
void manager_wiota_userid_deinit(void)
{
    if (RT_NULL == g_mng_userid_resource)
    {
        return;
    }

    // 遍历释放所有资源
    for (int subf_idx = 0; subf_idx < g_subframe_num; subf_idx++)
    {
        for (int slot_idx = 0; slot_idx < SLOT_NUM; slot_idx++)
        {
            for (int m = 0; m < HASH_LIST_LEN; m++)
            {
                clean_manager_list(&g_mng_userid_resource[subf_idx].mng_userid_list[slot_idx][m]);
                g_mng_userid_resource[subf_idx].count = 0;
            }
        }
    }

    g_reallocated_burst_idx = 0;
    g_reallocated_slot_idx = 0;
    g_reallocated_num = 0;

    manager_ul_time_slot_fn_init();

    rt_free(g_mng_userid_resource);
    g_mng_userid_resource = RT_NULL;
}

/**
 * @brief id管理初始化
 *
 * @param id_mng_cfg 地址管理配置
 * @return int       0:成功，其他:失败
 */
int manager_wiota_userid_init(id_manager_cfg_t *id_mng_cfg)
{
    RT_ASSERT(id_mng_cfg);
    RT_ASSERT(id_mng_cfg->addr_num);
    RT_ASSERT(id_mng_cfg->assign_end_addr > id_mng_cfg->assign_start_addr);
    RT_ASSERT(id_mng_cfg->assign_end_addr - id_mng_cfg->assign_start_addr > id_mng_cfg->addr_num);

    manager_address_init();

    if (0 != manager_calculate_userid_pos(id_mng_cfg))
    {
        return 2;
    }

    return 0;
}
